package service

import (
	"MyBlogv2/common/HttpCode"
	"MyBlogv2/common/models"
	"MyBlogv2/common/neo4jUtil"
	"MyBlogv2/common/response"
	"log"
	"strconv"

	"github.com/gin-gonic/gin"
)

// GetHomeRecommendArticles 主页推荐接口
// 已登录用户：使用个性化混合推荐（协同过滤50% + 基于内容50%），不足5个时用热门推荐补齐
// 未登录用户：降级到热门文章推荐
func GetHomeRecommendArticles(c *gin.Context) {
	limitStr := c.DefaultQuery("limit", "5")
	limit, _ := strconv.Atoi(limitStr)
	if limit <= 0 || limit > 50 {
		limit = 5
	}

	userId, exists := c.Get("userId")

	// 未登录用户：返回热门文章
	if !exists {
		log.Println("未登录用户，使用热门推荐")
		hotArticles, err := neo4jUtil.GetHotArticlesByNeo4j(limit)
		if err != nil || len(hotArticles) == 0 {
			log.Printf("Neo4j热门推荐失败，降级到默认推荐: %v", err)
			GetRecommendList(c)
			return
		}

		articleIds := extractArticleIds(hotArticles)
		articles := fetchArticlesByIds(articleIds, limit)
		response.ResponseOK(c, articles)
		return
	}

	// 已登录用户：个性化混合推荐
	log.Printf("用户 %d 使用个性化混合推荐", userId)

	// 1. 协同过滤推荐（50%）
	collaborativeArticles, err := neo4jUtil.GetRecommendArticlesByCollaborativeFiltering(userId.(int), limit/2)
	if err != nil {
		log.Printf("协同过滤推荐失败: %v", err)
	}

	// 2. 基于内容推荐（50%）
	contentArticles, err := neo4jUtil.GetRecommendArticlesByContent(userId.(int), limit/2)
	if err != nil {
		log.Printf("基于内容推荐失败: %v", err)
	}

	// 3. 合并去重
	articleIds := mergeAndDeduplicateArticleIds(collaborativeArticles, contentArticles)

	// 4. 如果个性化推荐不足limit个，用热门推荐补齐
	if len(articleIds) < limit {
		log.Printf("个性化推荐仅 %d 个，用热门推荐补齐到 %d 个", len(articleIds), limit)

		// 计算需要补充的数量
		needCount := limit - len(articleIds)
		// 多查询一些以防去重后不足
		hotArticles, err := neo4jUtil.GetHotArticlesByNeo4j(needCount * 2)
		if err != nil {
			log.Printf("热门推荐补充失败: %v", err)
		} else {
			// 提取热门文章ID并去重（排除已有的文章）
			existingIds := make(map[int]bool)
			for _, id := range articleIds {
				existingIds[id] = true
			}

			hotArticleIds := extractArticleIds(hotArticles)
			for _, id := range hotArticleIds {
				if !existingIds[id] {
					articleIds = append(articleIds, id)
					existingIds[id] = true
					if len(articleIds) >= limit {
						break
					}
				}
			}
			log.Printf("热门推荐补充后共 %d 个文章", len(articleIds))
		}
	}

	// 5. 如果仍然为空，降级到默认推荐
	if len(articleIds) == 0 {
		log.Println("所有推荐策略都失败，使用默认推荐")
		GetRecommendList(c)
		return
	}

	// 6. 从数据库获取文章详情
	articles := fetchArticlesByIds(articleIds, limit)
	response.ResponseOK(c, articles)
}

// GetSimilarArticles 文章详细页推荐相似文章接口
// 推荐策略：基于当前文章的分类，推荐同分类下的热门文章，不足5个时用全站热门补齐
func GetSimilarArticles(c *gin.Context) {
	articleIdStr := c.Param("articleId")
	articleId, err := strconv.Atoi(articleIdStr)
	if err != nil || articleId <= 0 {
		response.ResponseErrorWithMsg(c, HttpCode.FAILURE, "文章ID无效")
		return
	}

	limitStr := c.DefaultQuery("limit", "5")
	limit, _ := strconv.Atoi(limitStr)
	if limit <= 0 || limit > 20 {
		limit = 5
	}

	// 1. 使用Neo4j查询同分类的相似文章
	similarArticles, err := neo4jUtil.GetSimilarArticlesByArticleId(articleId, limit)
	if err != nil {
		log.Printf("Neo4j查询相似文章失败: %v", err)
		// Neo4j失败，降级到MySQL
		fallbackToMySQLSimilarArticles(c, articleId, limit)
		return
	}

	// 2. 提取文章ID
	articleIds := extractArticleIds(similarArticles)

	// 排除当前文章ID
	existingIds := make(map[int]bool)
	existingIds[articleId] = true
	filteredIds := make([]int, 0)
	for _, id := range articleIds {
		if !existingIds[id] {
			filteredIds = append(filteredIds, id)
			existingIds[id] = true
		}
	}
	articleIds = filteredIds

	// 3. 如果相似文章不足limit个，用热门文章补齐
	if len(articleIds) < limit {
		log.Printf("相似文章仅 %d 个，用热门文章补齐到 %d 个", len(articleIds), limit)

		needCount := limit - len(articleIds)
		// 多查询一些以防去重后不足
		hotArticles, err := neo4jUtil.GetHotArticlesByNeo4j(needCount * 2)
		if err != nil {
			log.Printf("热门文章补充失败: %v", err)
		} else {
			hotArticleIds := extractArticleIds(hotArticles)
			for _, id := range hotArticleIds {
				if !existingIds[id] {
					articleIds = append(articleIds, id)
					existingIds[id] = true
					if len(articleIds) >= limit {
						break
					}
				}
			}
			log.Printf("热门文章补充后共 %d 个推荐", len(articleIds))
		}
	}

	// 4. 如果仍然为空，降级到MySQL查询
	if len(articleIds) == 0 {
		log.Printf("文章 %d 所有Neo4j推荐策略都失败，使用MySQL降级", articleId)
		fallbackToMySQLSimilarArticles(c, articleId, limit)
		return
	}

	// 5. 从数据库获取文章详情
	articles := fetchArticlesByIds(articleIds, limit)
	response.ResponseOK(c, articles)
}

// fallbackToMySQLSimilarArticles MySQL降级策略：查询同分类文章
func fallbackToMySQLSimilarArticles(c *gin.Context, articleId int, limit int) {
	var article models.Article
	if err := models.Db.Where("id = ?", articleId).First(&article).Error; err != nil {
		response.ResponseErrorWithMsg(c, HttpCode.FAILURE, "文章不存在")
		return
	}

	var result []models.RecommendArticleVo
	models.Db.Table("t_article a").
		Select("a.id, a.article_title, a.article_cover, a.created_at").
		Joins("LEFT JOIN (SELECT type_id, COUNT(*) as like_count FROM t_like WHERE type = 1 GROUP BY type_id) l ON a.id = l.type_id").
		Where("a.category_id = ? AND a.id != ? AND a.status = 1 AND a.is_deleted = 0", article.CategoryId, articleId).
		Order("COALESCE(l.like_count, 0) DESC, a.created_at DESC").
		Limit(limit).
		Find(&result)

	response.ResponseOK(c, result)
}

// 辅助函数：从Neo4j结果中提取文章ID列表
func extractArticleIds(neo4jArticles []map[string]interface{}) []int {
	articleIds := make([]int, 0)
	for _, article := range neo4jArticles {
		if articleId, ok := article["articleId"].(int64); ok {
			articleIds = append(articleIds, int(articleId))
		}
	}
	return articleIds
}

// 辅助函数：合并并去重文章ID
func mergeAndDeduplicateArticleIds(lists ...[]map[string]interface{}) []int {
	seen := make(map[int]bool)
	articleIds := make([]int, 0)

	for _, list := range lists {
		for _, article := range list {
			if articleId, ok := article["articleId"].(int64); ok {
				id := int(articleId)
				if !seen[id] {
					articleIds = append(articleIds, id)
					seen[id] = true
				}
			}
		}
	}

	return articleIds
}

// 辅助函数：根据文章ID列表从数据库获取文章详情
func fetchArticlesByIds(articleIds []int, limit int) []models.RecommendArticleVo {
	if len(articleIds) == 0 {
		return []models.RecommendArticleVo{}
	}

	var articles []models.RecommendArticleVo
	models.Db.Table("t_article").
		Where("id IN ? AND status = 1 AND is_deleted = 0", articleIds).
		Limit(limit).
		Find(&articles)

	return articles
}
